package com.aplink.generic.autosync;

import java.util.ArrayList;

import android.accounts.Account;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.SyncResult;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;

import com.aplink.generic.constant.WebServiceContant;
import com.aplink.generic.constant.type.ActionType;
import com.aplink.generic.contentprovider.DataProvider;
import com.aplink.generic.controller.GenericApplication;
import com.aplink.generic.database.SqlMethods;
import com.aplink.generic.interfaces.OnHttpTaskPostExcute;
import com.aplink.generic.network.GenericFile;
import com.aplink.generic.pojo.ContentData;
import com.aplink.generic.pojo.GenericAttribute;
import com.aplink.generic.pojo.GenericObject;
import com.aplink.generic.pojo.MasterConfig;
import com.aplink.generic.pojo.ObjPropInfo;
import com.aplink.generic.pojo.TableSyncData;
import com.aplink.generic.sharepreference.GenericSharePreference;
import com.aplink.generic.util.LogUtil;
import com.aplink.generic.util.convertcount.GenericFilter;
import com.aplink.generic.webservice.DataResult;
import com.aplink.generic.webservice.HttpTask;
import com.aplink.generic.webservice.HttpURL;
import com.aplink.generic.xmlparser.ContentDataParser;

public class SyncAdapter extends AbstractThreadedSyncAdapter implements
        OnHttpTaskPostExcute {
    public static final int NOTIFICATION_ID = 1;
    private final ContentResolver mContentResolver;

    private MasterConfig mMasterConfig = null;

    private NotificationManager mNotificationManager;

    private final ArrayList<HttpURL> webserviceURLs;

    public SyncAdapter(final Context context, final boolean autoInitialize) {
        super(context, autoInitialize);
        this.mContentResolver = context.getContentResolver();
        this.webserviceURLs = new ArrayList<HttpURL>();
    }

    private String buildWhereCondition(final ContentData objPropCol,
            final GenericObject genericObject) {
        final String table = objPropCol.getMainTable();
        final ObjPropInfo createdTime = objPropCol.getCreatedTime(table);
        final SqlMethods sqlMethods = SqlMethods.getInstance();
        if (createdTime == null) {
            final ObjPropInfo primaryKey = objPropCol.getPrimaryKey(table);
            if (!TextUtils.isEmpty(genericObject.getValue(primaryKey
                    .getFullColumnName()))) {
                final String value = genericObject.getValue(
                        primaryKey.getFullColumnName()).toString();
                sqlMethods.equal(primaryKey, value);
            }
        } else {
            final String value = genericObject.getValue(
                    createdTime.getFullColumnName()).toString();
            sqlMethods.equal(createdTime, value);
        }
        return sqlMethods.toString();
    }

    public void clearSyncStatus(final GenericObject object,
            final ContentData objPropCol) {
        for (final ObjPropInfo objPropInfo : objPropCol.getSyncStatus()) {
            final String mainTableName = objPropInfo.getTable();
            final ContentData mainTableProperty = GenericFilter
                    .getObjPropColOf(mainTableName, objPropCol);
            final ObjPropInfo syncStatusColumn = objPropCol
                    .getSyncStatus(mainTableName);

            final Uri uri = Uri
                    .parse(getClearSyncContentURI(this.mMasterConfig) + "/"
                            + mainTableName);

            final SqlMethods whereClause = SqlMethods.getInstance().equal(
                    syncStatusColumn, "delete");

            this.mContentResolver.delete(uri, whereClause.toString(), null);

            final ContentValues values = new ContentValues();
            values.putNull(syncStatusColumn.getColumnName());
            final String where = buildWhereCondition(mainTableProperty, object);
            this.mContentResolver.update(uri, values, where, null);
        }
    }

    protected String getClearSyncContentURI(final MasterConfig masterConfig) {
        if (masterConfig.getAutoSync() != null) {
            return "content://" + masterConfig.getAutoSync().getAuthorities()
                    + "/" + DataProvider.CLEAR_SYNC;
        }
        return null;
    }

    protected String getTableContentURI(final MasterConfig masterConfig) {
        if (masterConfig.getAutoSync() != null) {
            return "content://" + masterConfig.getAutoSync().getAuthorities()
                    + "/" + DataProvider.TABLE;
        }
        return null;
    }

    private void initHttpURL() {
        this.mMasterConfig = GenericApplication.getInstance().getMasterConfig();

        for (int i = 0; i < this.mMasterConfig.getWebserviceConfig()
                .getTableDownloads().size(); i++) {
            final TableSyncData tableSyncData = this.mMasterConfig
                    .getWebserviceConfig().getTableDownloads().valueAt(i);
            if (tableSyncData.isDownloadOnly()) {
                continue;
            }
            final String mainTableName = tableSyncData.getTableName();
            final int contentDataFileId = tableSyncData
                    .getWebserviceFunctionID();
            final ContentData objPropCol = ContentDataParser.getInstance()
                    .parseIntoObjProp(contentDataFileId);
            final String syncStatusColumn = objPropCol.getSyncStatus(
                    tableSyncData.getTableName()).getFullColumnName();

            final SqlMethods sqlMethods = SqlMethods.getInstance().isNotNull(
                    syncStatusColumn);

            if (tableSyncData.isHavePhoto()) {
                final ObjPropInfo objPrimaryKey = objPropCol
                        .getPrimaryKey(mainTableName);
                final ObjPropInfo imageLocalPath = objPropCol
                        .getImageLocalPath();

                final String distinctColumn = objPrimaryKey.getFullColumnName();

                Uri uri = Uri.parse(getTableContentURI(this.mMasterConfig)
                        + "/" + tableSyncData.getTableName() + "/1");

                ArrayList<String> queriedColumns = new ArrayList<String>();
                for (int j = 0; j < objPropCol.size(); j++) {
                    final ObjPropInfo objPropInfo = objPropCol.valueAt(j);
                    if (objPropInfo.getTable().equalsIgnoreCase(
                            tableSyncData.getTableName())) {
                        queriedColumns.add(objPropInfo.getFullColumnName());
                    }
                }

                Cursor cursor = this.mContentResolver.query(uri, queriedColumns
                        .toArray(new String[queriedColumns.size()]), sqlMethods
                        .toString(), null, null);
                final ArrayList<GenericObject> distinctObjectList = selectDistinct(
                        cursor, tableSyncData.getTableName(), objPropCol);

                for (final GenericObject distinctObject : distinctObjectList) {
                    final String action = distinctObject.getValue(
                            syncStatusColumn).toString();
                    final HttpURL httpURL = new HttpURL();
                    httpURL.setObjPropCol(objPropCol)
                            .setDomain(
                                    this.mMasterConfig.getWebserviceConfig()
                                            .getWebserviceDomain())
                            .setTable(tableSyncData.getTableName())
                            .setAction(action).setJsonValue(distinctObject);

                    if (!objPropCol.getCreatedTimeObject().isEmpty()) {
                        if (!action.equalsIgnoreCase("insert")) {
                            final String columName = objPropCol.getCreatedTime(
                                    mainTableName).getFullColumnName();
                            final String value = distinctObject.getValue(
                                    columName).toString();
                            httpURL.setJsonCondition(value);
                        }
                    } else {
                        LogUtil.error(LogUtil.TAG_AUTO_SYNC,
                                "Created time is required for upload",
                                "Upload", getClass());
                        throw new NullPointerException(
                                "Created time is required for upload");
                    }

                    if (action.equalsIgnoreCase("delete")) {
                        httpURL.setWebserviceFunction(objPropCol
                                .getWebserviceFunction().replace(
                                        "ImageDataHandle?", "DataHandle?"));
                        httpURL.setMethod(WebServiceContant.GET);
                    } else {
                        httpURL.setWebserviceFunction(objPropCol
                                .getWebserviceFunction().replace("DataHandle?",
                                        "ImageDataHandle?"));

                        final String value = distinctObject.getValue(
                                distinctColumn).toString();

                        final SqlMethods whereClause = SqlMethods
                                .getInstance()
                                .equal(objPrimaryKey.getFullColumnName(), value);

                        final ArrayList<GenericFile> listUploadFiles = new ArrayList<GenericFile>();
                        final ArrayList<GenericFile> listDeleteFile = new ArrayList<GenericFile>();

                        final ObjPropInfo fileSyncStatus = objPropCol
                                .getSyncStatus(imageLocalPath.getTable());

                        whereClause.and().isNotNull(
                                fileSyncStatus.getFullColumnName());

                        uri = Uri.parse(getTableContentURI(this.mMasterConfig)
                                + "/" + objPropCol.getJoins());
                        queriedColumns = new ArrayList<String>();
                        for (int j = 0; j < objPropCol.size(); j++) {
                            final ObjPropInfo objPropInfo = objPropCol
                                    .valueAt(j);
                            if (objPropInfo.getTable().equalsIgnoreCase(
                                    fileSyncStatus.getTable())) {
                                queriedColumns.add(objPropInfo
                                        .getFullColumnName());
                            }
                        }

                        cursor = this.mContentResolver.query(uri,
                                queriedColumns
                                        .toArray(new String[queriedColumns
                                                .size()]), whereClause
                                        .toString(), null, null);

                        final ArrayList<GenericObject> images = selectDistinct(
                                cursor, fileSyncStatus.getTable(), objPropCol);

                        for (final GenericObject image : images) {
                            final String syncStatus = image.getValue(
                                    fileSyncStatus.getFullColumnName())
                                    .toString();

                            final String localPath = image.getValue(
                                    imageLocalPath.getFullColumnName())
                                    .toString();
                            final GenericFile file = new GenericFile();
                            file.setLocalPath(localPath);
                            file.setFileNameByURl(localPath);
                            if (syncStatus.equalsIgnoreCase("insert")) {
                                listUploadFiles.add(file);
                            } else {
                                listDeleteFile.add(file);
                            }
                        }

                        httpURL.setListUploadFiles(listUploadFiles);
                        httpURL.setListDeleteFiles(listDeleteFile);
                        httpURL.setMethod(WebServiceContant.POST);
                    }
                    this.webserviceURLs.add(httpURL);
                }
            } else {

                final Uri uri = Uri
                        .parse(getTableContentURI(this.mMasterConfig) + "/"
                                + tableSyncData.getTableName());

                final ArrayList<String> queriedColumns = new ArrayList<String>();
                for (int j = 0; j < objPropCol.size(); j++) {
                    final ObjPropInfo objPropInfo = objPropCol.valueAt(j);
                    if (objPropInfo.getTable().equalsIgnoreCase(
                            tableSyncData.getTableName())) {
                        queriedColumns.add(objPropInfo.getFullColumnName());
                    }
                }

                final Cursor cursor = this.mContentResolver
                        .query(uri, queriedColumns
                                .toArray(new String[queriedColumns.size()]),
                                sqlMethods.toString(), null, null);
                final ArrayList<GenericObject> genericObjects = selectDistinct(
                        cursor, tableSyncData.getTableName(), objPropCol);

                for (final GenericObject obj : genericObjects) {
                    final String action = obj.getValue(syncStatusColumn)
                            .toString();
                    final String domain = this.mMasterConfig
                            .getWebserviceConfig().getWebserviceDomain();
                    final String webserviceFunction = objPropCol
                            .getWebserviceFunction();
                    final HttpURL httpURL = new HttpURL();
                    httpURL.setObjPropCol(objPropCol).setDomain(domain)
                            .setWebserviceFunction(webserviceFunction)
                            .setTable(tableSyncData.getTableName())
                            .setAction(action).setJsonValue(obj);
                    if (!objPropCol.getCreatedTimeObject().isEmpty()) {
                        if (!action.equalsIgnoreCase("insert")) {
                            final String columName = objPropCol
                                    .getCreatedTimeObject().get(0)
                                    .getFullColumnName();
                            final String value = obj.getValue(columName)
                                    .toString();
                            httpURL.setJsonCondition(value);
                        }
                    }
                    httpURL.setMethod(WebServiceContant.GET);
                    this.webserviceURLs.add(httpURL);
                }
            }
        }
    }

    @Override
    public void onPerformSync(final Account account, final Bundle extras,
            final String authority, final ContentProviderClient provider,
            final SyncResult syncResult) {
        final boolean isPending = GenericSharePreference.getInstance(
                getContext()).isAutoSyncPending();
        final boolean isDatabaseModified = GenericSharePreference.getInstance(
                getContext()).isDatabaseModified();
        final boolean isEnableAutoSync = GenericSharePreference.getInstance(
                getContext()).isEnableAutoSync();
        final boolean isStartAutoSync = GenericSharePreference.getInstance(
                getContext()).isStartAutoSync();
        if (isEnableAutoSync && isStartAutoSync && !isPending
                && isDatabaseModified) {
            LogUtil.info("Auto-Sync is start", "onPerformSync", getClass());
            initHttpURL();
            if (!this.webserviceURLs.isEmpty()) {
                GenericSharePreference.getInstance(getContext())
                        .setAutoSyncPending(true);
                final HttpURL[] urls = this.webserviceURLs
                        .toArray(new HttpURL[this.webserviceURLs.size()]);
                final HttpTask httpTask = new HttpTask(ActionType.AUTO_SYNC);
                httpTask.setHttpTaskPostExcute(this);
                httpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, urls);
            }
        }
    }

    @Override
    public void onPostExecute(final ArrayList<DataResult> result) {
        if (!result.isEmpty()) {
            GenericSharePreference.getInstance(getContext())
                    .setAutoSyncPending(false);
            GenericSharePreference.getInstance(getContext())
                    .setDatabaseModified(false);
            GenericSharePreference.getInstance().setStartAutoSync(false);
            SyncUtils.getInstance().disableAutoSync();
            sendNotification();
            for (final DataResult dataResult : result) {
                final ContentData tableSyncData = dataResult.getContentData();
                final GenericObject genericObject = dataResult
                        .getGenericObject();
                clearSyncStatus(genericObject, tableSyncData);
            }
        }
    }

    private ArrayList<GenericObject> selectDistinct(Cursor cursor,
            final String table, final ContentData contentData) {
        final ArrayList<GenericObject> genericObjectList = new ArrayList<GenericObject>();
        if (cursor != null) {
            cursor.moveToFirst();
            final int numRows = cursor.getCount();
            for (int i = 0; i < numRows; ++i) {
                final GenericObject genericObject = new GenericObject();
                genericObject.setTableName(table);
                for (int j = 0; j < contentData.size(); j++) {
                    final ObjPropInfo objPropInfo = contentData.valueAt(j);
                    if (objPropInfo.getTable().equalsIgnoreCase(table)) {
                        final String name = objPropInfo.getColumnName();
                        String value = cursor.getString(cursor
                                .getColumnIndex(name));
                        if (value == null) {
                            value = "";
                        }
                        final GenericAttribute attribute = GenericAttribute
                                .obtain(value, objPropInfo);
                        genericObject.put(objPropInfo.getFullColumnName(),
                                attribute);
                    }
                }
                genericObjectList.add(genericObject);
                cursor.moveToNext();
            }
            cursor.close();
            cursor = null;
            return genericObjectList;
        }
        return null;
    }

    private void sendNotification() {
        this.mNotificationManager = (NotificationManager) getContext()
                .getSystemService(Context.NOTIFICATION_SERVICE);
        final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
                getContext())
                .setAutoCancel(true)
                .setContentTitle(
                        this.mMasterConfig.getAutoSync().getContentTitle())
                .setSmallIcon(this.mMasterConfig.getAutoSync().getIconID())
                .setDefaults(
                        Notification.DEFAULT_LIGHTS
                                | Notification.DEFAULT_SOUND)
                .setContentText(this.mMasterConfig.getAutoSync().getMessage());
        this.mNotificationManager.notify(SyncAdapter.NOTIFICATION_ID,
                mBuilder.build());
    }
}
